<template>
  <div class="page-workflowdrawer">
    <!-- 左侧树 -->
    <div class="drawer-left">
      <ul class="sub-nav">
        <li title="数据" :class="{'active': type==='data'}" @click="changeType('data')">
          <i class="iconfont icon-data2"/>
        </li>
        <li title="组件" :class="{'active': type==='comp'}" @click="changeType('comp')">
          <i class="iconfont icon-comp"/>
        </li>
      </ul>
      <div class="tree-container">
        <input type="text" class="aimind-hidden-input">
        <input type="password" class="aimind-hidden-input">
        <Input
          v-model.trim="searchValue"
          placeholder="请输入工作流组件名称"
          :search="true"
          @on-search="handleTreeSearch"
        />
        <Tree
          class="workflow-tree"
          :data="compTreeData"
          :render="compTreeRender"
          v-if="type==='comp'"
        ></Tree>

        <!-- 新增节点 -->
        <div
          v-if="type==='comp'"
          class="workflow-tree-node border-node add-node"
          @mousedown="addNewNode"
        >
          <span>
            <i class="iconfont icon-add"></i>
            <span title="新增节点" class="workflow-tree-node-main">新增节点</span>
          </span>
        </div>
        <div class="no-data" v-if="type==='data'">暂无数据</div>
      </div>
    </div>
    <!-- 中间拖拽区域 -->
    <div class="drawer-container">
      <div
        :class="{'drawer-container-top': true,'drawer-container-top200':logShow,'drawer-container-top300':logShow && currentNode && currentNode.id && logContent[currentNode.id]}"
      >
        <div class="svg-contain" ref="svgContain" @click="svgContainClick" @scroll="onScroll">
          <!-- 节点渲染  -->
          <div
            class="svg"
            ref="region"
            :style="{'width': `${canvasX}px`, 'height': `${canvasY}px`}"
          >
            <svg
              @mousedown.self="canvasDrag"
              xmlns="http://www.w3.org/2000/svg"
              version="1.1"
              class="pane-svg"
              width="100%"
              height="100%"
            >
              <g class="pane-viewport" :transform="`scale(${scale},${scale})`">
                <PaneLink
                  v-for="(v) in link"
                  :key="`${v.start}-${v.end}-${v.desIndex}`"
                  :item="v"
                  @coverShow="linkCoverShow"
                ></PaneLink>

                <PaneNode
                  v-for="(v, k) in node"
                  :item="v"
                  :key="k"
                  :isNewLink="isNewLink"
                  :bounds="bounds"
                  :scrollX="scrollX"
                  :scrollY="scrollY"
                  :spaceX="spaceX"
                  :spaceY="spaceY"
                  :activeNode="activeNode"
                  :flowNode="flowNode"
                  :compatibleToDataFlow="compatibleToDataFlow"
                  :scale="scale"
                  @coverShow="coverShow"
                  @nodeChoose="nodeChoose"
                  @paneNodeChange="paneNodeChange"
                  @linkChange="linkChange"
                  @isConnect="isConnect"
                  @nodeDragEnd="nodeDragEnd"
                ></PaneNode>
              </g>
            </svg>
          </div>
        </div>
      </div>
      <div class="drawer-container-bottom">
        <div v-show="logShow" class="log-wrapper">
          <Icon class="top-desktop" type="md-desktop" @click="logShow=!logShow"/>
          <template v-if="currentNode && currentNode.id && logContent[currentNode.id]">
            <Tabs v-model="runlog">
              <TabPane label="日志" name="running">
                <div v-for="(value,key) of logContent" :key="key">
                  <template v-if="key === currentNode.id">
                    <div id="logContent" class="log-content">
                      <strong>【{{currentNode.name}}】</strong>
                      <p v-for="(unit,order) of value" :key="order" v-html="getHtmlColor(unit)"></p>
                    </div>
                  </template>
                </div>
              </TabPane>
            </Tabs>
          </template>
          <div v-else>
            <Tabs value="log" type="card">
              <TabPane label="运行日志" name="log">
                <div class="text-log">
                  运行状态：{{logStatus ? logStatus :"未运行"}}
                  <br>
                  组件个数：{{Object.keys(node).length}}
                  <br>
                  当前实验完成进度：{{successNodeLength}} / {{Object.keys(node).length}}
                  <br>注：点击画布中的组件可查看运行状态
                </div>
              </TabPane>
            </Tabs>
          </div>
        </div>
        <Icon type="md-desktop" @click="logIconClick" v-show="!logShow"/>
      </div>

      <!-- 运行 -->
      <div class="drawer-operate">
        <Button @click.stop="stopWorkflow" v-if="logRun">
          <i class="iconfont icon-stop"></i>停止
        </Button>
        <Button @click.stop="runWorkflow" v-else>
          <Icon type="md-arrow-dropright-circle"/>运行
        </Button>
      </div>

      <!-- 导出 -->
      <div class="drawer-operate-right">
        <Button type="text" @click.stop="exportWorkflow">
          <Icon type="ios-cloud-download-outline"/>导入已有工作流
        </Button>
      </div>
    </div>
    <!-- 设置面板 -->
    <div class="drawer-right">
      <DrawerSetting ref="drawerSetting" :nodeInfo="chooseNodeInfo"/>
    </div>

    <!-- 正在拖拽的组件 -->
    <drag
      @mouseup="onEnd"
      ref="node"
      :x="currentX"
      :y="currentY"
      :class="{'drag':true, 'active': animating, 'show': newNode}"
    >
      <div class="component-drag-proxy">
        <i :class="['iconfont',currentNode ? currentNode.nodeIcon:'icon-datas']"></i>
        <span class="text">{{currentNode ? (currentNode.name || currentNode.title) : ''}}</span>
      </div>
    </drag>

    <!-- 右击菜单节点 -->
    <div
      v-show="cover"
      class="ui-cover"
      @contextmenu.prevent="() => false"
      @click.self="cover=false"
    >
      <ul :style="{'left': `${coverX}px`,'top': `${coverY}px`}">
        <li @click="handelRename">
          <i class="iconfont icon-edit"></i>
          <span>重命名</span>
        </li>
        <li @click="handelDel">
          <i class="iconfont icon-trash"></i>
          <span>删除节点</span>
        </li>
        <li @click="handelRun" v-show="pipelineJobId && logStatus !== 'RUNNING'">
          <i class="iconfont icon-right"></i>
          <span>从当前节点运行</span>
        </li>
      </ul>
    </div>

    <!-- 右击菜单链接 -->
    <div
      v-show="linkcover"
      class="ui-cover"
      @contextmenu.prevent="() => false"
      @click.self="linkcover=false"
    >
      <ul :style="{'left': `${coverX}px`,'top': `${coverY}px`}">
        <li @click="handelLinkDel">
          <i class="iconfont icon-trash"></i>
          <span>删除连接线</span>
        </li>
      </ul>
    </div>

    <!-- 重命名 -->
    <Modal
      footer-hide
      title="重命名"
      v-model="renameVisible"
      width="500px"
      class="workflow-rename-model"
    >
      <span>请输入新的节点名称</span>
      <Input v-model.trim="newName" placeholder="请输入节点名称" :maxlength="20"/>
      <div class="rename-error" v-show="newNameError">
        <Icon type="ios-information-circle-outline"/>请输入节点名称
      </div>
      <div class="rename-footer">
        <Button @click="renameVisible=false">取消</Button>
        <Button type="primary" @click="renameOkHandler">确定</Button>
      </div>
    </Modal>

    <!-- 选择工作流 -->
    <Modal footer-hide title="选择工作流" v-model="selectWorkflowVisible" width="600px">
      <Table
        class="workflowdrawer-table"
        :columns="workflowColumns"
        :data="workflowData"
        :disabled-hover="true"
        :highlight-row="true"
        @on-row-click="workflowSelect"
        size="small"
      >
        <template slot-scope="{ row }" slot="createTime">
          <strong>{{ row.createTime ? formatDateByStringMatch(row.createTime) :'NA' }}</strong>
        </template>
      </Table>

      <Page
        style="text-align:right;margin-top:10px"
        v-if="total > 10"
        :page-size="10"
        :total="total"
        size="small"
        @on-change="changePage"
      />

      <div class="workflow-footer">
        <Button @click="workflowCancel" style="margin-right:10px;">取消</Button>
        <Button type="primary" @click="workflowOk">确定</Button>
      </div>
    </Modal>
  </div>
</template>

<script>
import { mapMutations, mapState } from 'vuex';
import { deepClone, formatDateByStringMatch } from 'assets/scripts/common';
import { wfCode } from 'assets/scripts/code';

import Bus from 'assets/scripts/bus';

import DrawerSetting from './DrawerSetting.vue';
import Drag from './Dragable/Drag';
import PaneNode from './Dragable/PaneNode';
import PaneLink from './Dragable/PaneLink';

import * as workflowApi from 'http/api/workflow';
import { getPipeline } from 'http/api/pipeline';

const AimindStart = ':: Starting....  ::';
const AimindEnd = ':: AIMind, do the best kgms ! ::';

export default {
  components: {
    DrawerSetting,
    Drag,
    PaneNode,
    PaneLink
  },
  computed: {
    ...mapState({
      compTreeData: state => state.workflow.compTreeData,
      navCollapsed: state => state.app.navCollapsed,
      currentNode: state => state.workflow.currentNode,
      link: state => state.workflow.link,
      node: state => state.workflow.node,
      autoDatas: state => state.workflow.autoDatas
    })
  },
  watch: {
    navCollapsed () {
      this.spaceX = this.navCollapsed ? 90 : 240;
    }
  },
  data () {
    return {
      // 表示数据/组件 data数据 comp组件
      type: 'comp',
      searchValue: '',
      logShow: false, // 显示面板标识
      logRun: false, // 是否运行标识
      runlog: 'running', // 日志显示面板
      errorLabel: h => {
        return h('div', [
          h('span', '错误'),
          h('Badge', {
            props: {
              count: this.errorLogContent.length
            }
          })
        ]);
      },
      warnLabel: h => {
        return h('div', [
          h('span', '警告'),
          h('Badge', {
            props: {
              count: this.warnLogContent.length
            }
          })
        ]);
      },
      logContent: {},
      errorLogContent: [],
      warnLogContent: [],

      // 画布的参数
      originX: 0,
      originY: 0,
      currentX: 0,
      currentY: 0,
      scrollX: 0,
      scrollY: 0,

      newNode: false,
      animating: false,
      scale: 1,
      bounds: {},
      canvasX: 0,
      canvasY: 0,

      isNewLink: false,
      activeNode: '',
      chooseNodeInfo: null,

      // 右键菜单
      coverX: 0,
      coverY: 0,
      contextNodeId: null,
      cover: false,
      linkcover: false,
      linkContextNode: null,

      // 距离边界的距离
      spaceY: 64, // 距离顶部的距离
      spaceX: 90, // 距离左侧暗色的导航距离
      renameVisible: false,
      newName: '',
      newNameError: false,
      pipelineId: '',
      pipelineJobId: '',
      logTimer: null,
      logStatus: '',
      successNodeLength: 0,
      selectWorkflowVisible: false,
      formatDateByStringMatch: formatDateByStringMatch,
      workflowColumns: [
        {
          title: '工作流名称',
          key: 'name',
          ellipsis: true,
          tooltip: true
        },
        {
          title: '创建时间',
          key: 'createTime',
          slot: 'createTime'
        }
      ],
      workflowData: [],
      workflowSelectId: null,
      pageIndex: 1,
      total: 0,
      flowNode: null,
      compatibleToDataFlow: false
    };
  },
  methods: {
    ...mapMutations([
      'setComTreeData',
      'changeSettingType',
      'setNavCollapsed',
      'setCurrentNode',
      'setLink',
      'setNode',
      'setAutoDatas',
      'setNodeStatus'
    ]),

    // 确定重命名
    renameOkHandler () {
      if (!this.newName) {
        this.newNameError = true;
        return;
      }

      workflowApi
        .renameNode({
          name: this.newName,
          pipelineTaskId: this.contextNodeId
        })
        .then(res => {
          this.newNameError = false;
          this.renameVisible = false;
          this.cover = false;

          // 手动更新名称
          this.node[this.contextNodeId].name = this.newName;
          this.setNode(this.node);

          this.chooseNodeInfo = this.node[this.contextNodeId];

          this.$Message.success('命名成功');
        });
    },

    // 运行
    runWorkflow () {
      // 如果没有选择运行环境，不可以运行
      let canNotRunName = '';
      for (let i in this.node) {
        let { pipelineTaskNodeType, taskType, name } = this.node[i];
        if (
          (pipelineTaskNodeType === wfCode.MULTIPLEXT ||
            pipelineTaskNodeType === wfCode.CUSTOM) &&
          (!taskType || (!taskType && !taskType.id))
        ) {
          canNotRunName = name;
          break;
        }
      }

      if (canNotRunName) {
        this.$Message.error(`请先给节点(${canNotRunName})设置运行环境`);
        return;
      }

      this.$Modal.confirm({
        title: '运行提示',
        content: '确认运行工作流吗？',
        onOk: () => {
          workflowApi
            .runPipeline({
              id: this.pipelineId
            })
            .then(res => {
              if (res) {
                this.pipelineJobId = res.data.id;
                this.$Message.success('启动成功');
                localStorage[`stopId${this.pipelineId}`] = res.data.id;
                // 状态全部还原
                for (let i in this.node) {
                  this.setNodeStatus({
                    id: i,
                    status: ''
                  });
                }
                this.startRunWorkflow(true);
              }
            });
        },
        onCancel: () => {}
      });
    },

    /**
     * 获取带颜色的文本
     */
    getHtmlColor (text) {
      if (text.indexOf('success') > -1 || text.indexOf('SUCCESS') > -1) {
        return `<span style="color:#19be6b">${text}</span>`;
      }

      if (
        text.indexOf('fail') > -1 ||
        text.indexOf('error') > -1 ||
        text.indexOf('FAIL') > -1 ||
        text.indexOf('ERROR') > -1
      ) {
        return `<span style="color:#ed4014">${text}</span>`;
      }

      // AIMIND
      if (text === AimindStart || text === AimindEnd || /^[# ]+$/.test(text)) {
        text = text.replace(/\s/g, '&nbsp;&nbsp;').replace(/\r?\n/g, '<br/>');
        return `<span style="color:#2d8cf0">${text}</span>`;
      }

      return text;
    },

    // 获取日志信息
    getLogs () {
      let that = this;
      if (this.logStatus === 'RUNNING' || !this.logStatus) {
        workflowApi
          .getLogs({
            id: this.pipelineJobId
          })
          .then(res => {
            if (res && res.data && res.data.logs) {
              // 成功的节点记录
              that.successNodeLength = 0;

              // 节点的日志记录
              that.logContent = {};

              res.data.logs.forEach(item => {
                let { pipelineTaskId, status } = item;

                if (that.node[pipelineTaskId]) {
                  that.setNodeStatus({
                    id: pipelineTaskId,
                    status
                  });
                }
                if (status === 'SUCCESS') {
                  that.successNodeLength = that.successNodeLength + 1;
                }
                that.logContent[item.pipelineTaskId] = item.logs;
              });

              that.logStatus = res.data.status;
              if (document.getElementById('logContent')) {
                document.getElementById('logContent').scrollTop += 200;
              }
            }
          });
      } else {
        clearInterval(this.logTimer);
        this.logTimer = null;
        this.logRun = false;
      }
    },

    // 开始运行工作流
    startRunWorkflow (boolean) {
      // 启动运行
      this.logRun = true;

      // 日志查看
      this.logShow = boolean;

      // 获取日志跑日志
      this.logStatus = 'RUNNING';

      if (!this.logTimer) {
        this.getLogs();
        this.logTimer = setInterval(() => {
          this.getLogs();
        }, 2000);
      }
    },

    // 点击获取日志
    logIconClick () {
      this.logShow = true;
    },

    // 停止
    stopWorkflow () {
      this.$Modal.confirm({
        title: '停止提示',
        content: '确认停止工作流运行吗？',
        onOk: () => {
          workflowApi
            .stopPipeline({
              id: this.pipelineJobId || localStorage[`stopId${this.pipelineId}`]
            })
            .then(res => {
              if (res) {
                delete localStorage[`stopId${this.pipelineId}`]
                this.$Message.success('已停止运行');
                this.logRun = false;
                clearInterval(this.logTimer);
                this.logTimer = null;

                // 节点的状态改变,如果是运行状态下，修改之
                setTimeout(() => {
                  for (let i in this.node) {
                    if (this.node[i].status === 'RUNNING') {
                      this.setNodeStatus({
                        id: i,
                        status: ''
                      });
                    }
                  }
                }, 200);
              }
            });
        },
        onCancel: () => {}
      });
    },

    // 导出已有工作流
    exportWorkflow () {
      // 请求工作流的数据
      getPipeline({
        name: '',
        kgId: this.$route.query.id,
        pageIndex: this.pageIndex,
        pageSize: 10
      }).then(res => {
        if (res) {
          this.total = res.data.total;
          this.workflowData = res.data.pipelineStates;
          this.selectWorkflowVisible = true;

          if (!this.workflowSelectId) {
            this.workflowSelectId = this.$route.query.pipelineId;
          }
        }
      });
    },

    // 工作流的分页切换
    changePage (num) {
      this.pageIndex = num;
      this.exportWorkflow();
    },

    // 选择工作流
    workflowSelect (row) {
      this.workflowSelectId = row.id;
    },

    // 关闭工作流列表
    workflowCancel () {
      this.selectWorkflowVisible = false;
    },

    // 切换工作流
    workflowOk () {
      if (this.workflowSelectId === this.$route.query.pipelineId) {
        this.$Message.error('您选择的是当前工作流');
        return;
      }

      // 二次提醒
      this.$Modal.confirm({
        title: '导入提示',
        content: '确认导入选中的工作流吗？',
        zIndex: '9999',
        onOk: async () => {
          // 复制
          workflowApi
            .importPipeline({
              sourceId: this.$route.query.pipelineId,
              targetId: this.workflowSelectId
            })
            .then(res => {
              if (res && res.data) {
                // 切换至工作流属性
                this.changeSettingType(wfCode.MAIN);

                this.$nextTick(() => {
                  // 获取数据并重绘
                  this.redrawNodes('导入成功');
                });

                this.selectWorkflowVisible = false;
              }
            });
        },
        onCancel: () => {}
      });
    },

    /**
     * 更改节点类型
     * @param type 类型
     */
    changeType (type) {
      this.type = type;
    },

    /**
     * 检索并控制搜索时相应的节点以及其祖先节点的打开状态（深度遍历链）
     * @param v 点击的树值
     */
    handleTreeSearch (v) {
      this.searchValue = v;
      if (!v) {
        return;
      }
      // 切换 expand 状态
      const loop = (node, depthList) => {
        const { children } = node;
        depthList.push(node);
        if (v && children) {
          const len = children.length;
          for (let i = 0; i < len; i++) {
            if (children[i].title.indexOf(v) > -1) {
              depthList.forEach(item => (item.expand = true));
            }
            loop(children[i], [...depthList]);
          }
        }
      };

      const treeData = deepClone(this.compTreeData);
      loop(
        {
          children: treeData,
          name: ''
        },
        []
      );
      this.setComTreeData(treeData);
    },

    // 树节点绘制 render 函数
    compTreeRender (h, { root, node, data }) {
      const { selected, title: name } = data;
      let searchIndex = -1;
      let beforeStr = '';
      let afterStr = '';

      // 搜索词拆分逻辑
      if (name) {
        searchIndex = name
          .toLowerCase()
          .indexOf(this.searchValue.toLowerCase());
        beforeStr = name.substr(0, searchIndex);
        afterStr = name.substr(searchIndex + this.searchValue.length);
      }

      // 根据搜索词定制关键字render
      let nameRender = name;
      if (this.searchValue && searchIndex > -1) {
        nameRender = h('span', [
          beforeStr,
          h(
            'span',
            {
              style: 'color: #f50'
            },
            this.searchValue
          ),
          afterStr
        ]);
      }
      return h(
        'div',
        {
          class: `workflow-tree-node${
            data.categoryId || data.isCustom ? ' border-node' : ''
          }`,
          on: {
            mousedown: e => {
              if (!data.categoryId && !data.isCustom) {
                return;
              }
              this.nodeMouseDown(e, data);
            }
          }
        },
        [
          h('span', [
            h('i', {
              class:
                data.categoryId || data.isCustom
                  ? `iconfont ${data.nodeIcon ? data.nodeIcon : 'icon-datas'}`
                  : '',
              style: {
                position: 'relative',
                top: '-4px',
                left: '5px',
                fontSize: '14px'
              }
            }),
            h(
              'span',
              {
                class: `workflow-tree-node-main${selected ? ' selected' : ''}`,
                attrs: {
                  title: data.desc ? data.desc : nameRender
                }
              },
              [nameRender]
            )
          ])
        ]
      );
    },

    // 创建一个新的节点
    addNewNode (e) {
      let data = {
        id: `node${new Date().getTime()}`,
        title: '新增节点',
        newNode: true,
        nodeIcon: 'icon-add',
        dataFlow: false
      };
      this.nodeMouseDown(e, data);
    },

    /**
     * 树节点点击
     * @param data 点击的树数据
     */
    nodeMouseDown (e, data) {
      let lX = e.pageX - this.spaceX;
      let lY = e.pageY - this.spaceY;
      this.originX = lX;
      this.originY = lY;
      this.currentX = lX;
      this.currentY = lY;
      this.setCurrentNode(data);
      document.body.style.userSelect = 'none';
      document.addEventListener('mousemove', this.move);
      document.addEventListener('mouseup', this.onEnd);
    },

    // 节点移动
    move (event) {
      this.newNode = true;
      this.animating = true;
      this.currentX = event.pageX - this.spaceX;
      this.currentY = event.pageY - this.spaceY;
    },

    // 新增节点结束
    onEnd (event) {
      const leftWidth = 280;
      let $svgContain = this.$refs.svgContain;
      let svgWidth = $svgContain.offsetWidth;
      let svgHeight = $svgContain.offsetHeight;
      let svgScrollLeft = $svgContain.scrollLeft;
      let svgScrollTop = $svgContain.scrollTop;

      // 280是左侧的宽度
      if (
        this.currentX > leftWidth &&
        this.currentX < svgWidth + leftWidth &&
        this.currentY > 0 &&
        this.currentY < svgHeight - 40
      ) {
        let {
          isCustom,
          title,
          id,
          categoryId,
          newNode,
          nodeIcon
        } = this.currentNode;

        // 创建节点
        let item = {
          status: '',
          id: id,
          name: title,
          categoryId: categoryId,
          isCustom: isCustom,
          newNode: newNode,
          nodeIcon: nodeIcon,
          isConnected: false,
          x: (this.currentX - leftWidth + svgScrollLeft) / this.scale,
          y: (this.currentY + svgScrollTop) / this.scale
        };

        // 设置成当前激活节点
        this.setCurrentNode(item);

        // 请求接口1-自定义节点
        // 请求接口2-拖拽节点
        this.changeSettingType(null);
        this.chooseNodeInfo = null;

        this.$nextTick(() => {
          newNode ? this.createCustomNode(item) : this.createDragNode(item);
        });
      } else {
        this.animating = false;
        this.activeNode = '';

        this.setCurrentNode(null);
        this.resetDragStatus();
      }

      document.body.style.userSelect = '';
      document.removeEventListener('mousemove', this.move);
      document.removeEventListener('mouseup', this.onEnd);
    },

    // 重置状态
    resetDragStatus () {
      this.currentX = this.originX;
      this.currentY = this.originY;
      this.newNode = false;
    },

    /**
     * 创建自定义节点
     * @param item 节点数据
     */
    createCustomNode (item) {
      let { x, y, name } = item;
      workflowApi
        .createCustomNode({
          location: {
            x,
            y
          },
          pipeline: {
            id: this.pipelineId
          },
          pipelineTaskNodeType: wfCode.CUSTOM,
          name
        })
        .then(res => {
          if (res && res.data) {
            let { id } = res.data;
            this.currentNode.id = id;

            item.id = id;

            // 设置自定义面板
            this.changeSettingType(wfCode.CUSTOM);
            this.setCurrentNode(item);
            this.$set(this.node, id, item);
            this.setNode(this.node);
            this.resetDragStatus();
            this.activeNode = this.currentNode.id;
          }
        })
        .catch(() => {
          // 创建失败回到工作流界面
          this.setCurrentNode(null);
          this.changeSettingType(wfCode.MAIN);
          this.resetDragStatus();
        });
    },

    /**
     * 创建普通节点
     * @param item 节点数据
     */
    createDragNode (item) {
      let pipelineTaskNodeType;

      // 设置自定义面板
      let { categoryId, isCustom } = this.currentNode;

      if (isCustom) {
        this.changeSettingType(wfCode.MULTIPLEXT);
        pipelineTaskNodeType = wfCode.MULTIPLEXT;
      } else {
        switch (categoryId) {
          // 数据输入
          case wfCode.IMPORT:
            this.changeSettingType(wfCode.IMPORT);
            pipelineTaskNodeType = wfCode.IMPORT;
            break;
          // 数据输出
          case wfCode.EXPORT:
            this.changeSettingType(wfCode.EXPORT);
            pipelineTaskNodeType = wfCode.EXPORT;
            break;
          default:
            this.changeSettingType(wfCode.COMMON);
            pipelineTaskNodeType = wfCode.COMMON;
        }
      }

      let { x, y, name } = item;
      workflowApi
        .createDragNode({
          pipelineId: this.pipelineId,
          taskNodeId: item.id,
          pipelineTaskNodeType,
          name,
          location: {
            x,
            y
          }
        })
        .then(res => {
          if (!res) {
            return;
          }
          let { id } = res.data;
          this.currentNode.id = id;
          item.id = id;
          item = Object.assign({}, res.data, item);
          this.chooseNodeInfo = item;

          this.$set(this.node, id, item);
          this.setNode(this.node);
          this.setCurrentNode(item);
          this.resetDragStatus();
          this.activeNode = this.currentNode.id;

          let { pipelineTaskNodeType, argValues, args } = this.node[id];
          this.addAutoDatas(pipelineTaskNodeType, argValues, args);
        })
        .catch(() => {
          // 回到工作流界面
          this.setCurrentNode(null);
          this.changeSettingType(wfCode.MAIN);
          this.resetDragStatus();
        });
    },

    // 计算画布的大小
    computedCanvas () {
      let $svgContain = this.$refs.svgContain;
      let minWidth = $svgContain ? $svgContain.offsetWidth : 0;
      let minHeight = $svgContain ? $svgContain.offsetHeight : 0;

      // 获得已知node的边距
      let maxNodeX = 0;
      let maxNodeY = 0;
      for (let key in this.node) {
        if (this.node[key].x > maxNodeX) maxNodeX = this.node[key].x;
        if (this.node[key].y > maxNodeY) maxNodeY = this.node[key].y;
      }

      // 判断所有节点x,y坐标值和最小画布的大小
      this.canvasX =
        (maxNodeX + 180) * this.scale > minWidth
          ? (maxNodeX + 180) * this.scale
          : minWidth;

      // this.canvasX = minWidth;
      this.canvasY =
        (maxNodeY + 30) * this.scale + 170 > minHeight
          ? (maxNodeY + 30) * this.scale + 170
          : minHeight;

      this.canvasY = this.canvasY - 10;

      this.$set(this.bounds, 'right', this.canvasX);
      this.$set(this.bounds, 'bottom', this.canvasY);
      this.$set(this.bounds, 'top', 0);
      this.$set(this.bounds, 'left', 0);

      // this.resize(this.canvasX, this.canvasY);
    },

    // 画布变化
    scaleChange (scale) {
      this.scale = scale === 0 ? 1 : this.scale + scale;
      this.computedCanvas();
    },

    // 窗口变换
    resize (
      x = this.$refs.region ? this.$refs.region.offsetWidth : 0,
      y = this.$refs.region ? this.$refs.region.offsetHeight : 0
    ) {
      this.$nextTick(() => {
        this.computedCanvas();
      });
    },

    // 获得滚动条距离
    onScroll () {
      let $svgContain = this.$refs.svgContain;
      this.scrollX = $svgContain ? $svgContain.scrollLeft : 0;
      this.scrollY = $svgContain ? $svgContain.scrollTop : 0;
    },

    // 画布拖拽
    canvasDrag (e) {
      this.currentX = e.pageX;
      this.currentY = e.pageY;
      document.body.style.userSelect = 'none';
      document.addEventListener('mousemove', this.canvasMove);
      document.addEventListener('mouseup', this.canvasEnd);
    },

    // 画布移动
    canvasMove (e) {
      let delx = -(e.pageX - this.currentX) + this.$refs.svgContain.scrollLeft;
      let dely = -(e.pageY - this.currentY) + this.$refs.svgContain.scrollTop;
      this.$refs.svgContain.scrollTo(delx, dely);
      this.currentX = e.pageX;
      this.currentY = e.pageY;
    },

    // 画布拖拽结束
    canvasEnd (e) {
      document.body.style.userSelect = '';
      document.removeEventListener('mousemove', this.canvasMove);
      document.removeEventListener('mouseup', this.canvasEnd);
    },

    /**
     * 节点的选择
     * @param item 节点数据
     */
    nodeChoose (item) {
      if (this.activeNode === item.id) {
        return;
      }
      this.activeNode = item.id;
      this.setCurrentNode(item);

      let { categoryId, isCustom, newNode, pipelineTaskNodeType } = item;
      this.changeSettingType(null);

      this.chooseNodeInfo = null;

      // 获取信息并显示
      this.$nextTick(() => {
        // 新的节点
        if (newNode || pipelineTaskNodeType === wfCode.CUSTOM) {
          this.changeSettingType(wfCode.CUSTOM);
          // 数据输入
        } else if (
          categoryId === wfCode.IMPORT ||
          pipelineTaskNodeType === wfCode.IMPORT
        ) {
          this.changeSettingType(wfCode.IMPORT);
          // 数据输出
        } else if (
          categoryId === wfCode.EXPORT ||
          pipelineTaskNodeType === wfCode.EXPORT
        ) {
          this.changeSettingType(wfCode.EXPORT);
          // 可复用
        } else if (isCustom || pipelineTaskNodeType === wfCode.MULTIPLEXT) {
          this.changeSettingType(wfCode.MULTIPLEXT);
        } else {
          this.changeSettingType(wfCode.COMMON);
        }
        this.chooseNodeInfo = item;
      });
    },

    /**
     * 节点位置变化
     * @param item 节点数据
     */
    paneNodeChange (data) {
      this.$set(this.node, data.id, {
        ...this.node[data.id],
        x: data.x,
        y: data.y
      });
      this.setCurrentNode(data);
      this.setNode(this.node);
      this.linkReview();
      this.computedCanvas();
    },

    /**
     * 节点位置拖拽结束
     * @param item 节点数据
     */
    nodeDragEnd (item) {
      if (item) {
        let { location, x, y } = item;
        if (location && (location.x !== x || location.y !== y)) {
          item.location = {
            x,
            y
          };
          workflowApi.updateCustomNode(item).then(res => {});
        }
      } else {
        if (this.$refs.drawerSetting) {
          this.$refs.drawerSetting.updateNodePosition();
        }
      }
    },

    // link重绘
    linkReview () {
      let link = [...this.link];
      link.forEach((v, k) => {
        if (!v.end) return false;

        let startNode = this.node[v.start];
        let desNode = this.node[v.end];

        // 默认修改
        this.$set(link[k], 'startPosY', startNode.y);
        this.$set(link[k], 'endPosY', desNode && desNode.y ? desNode.y : null);

        // 起始点的流数据设置
        let dataFlow = startNode.systemNode
          ? startNode.systemNode.dataFlow
          : false;
        let outputPorts = startNode.systemNode
          ? startNode.systemNode.outputPorts
          : [];

        if (dataFlow) {
          let startPosX = this.getXPos(
            startNode.x,
            outputPorts.length,
            v.srcIndex
          );
          this.$set(link[k], 'startPosX', startPosX);
        } else {
          this.$set(link[k], 'startPosX', startNode.x + 90);
        }

        // 末尾点的流数据设置
        let endDataFlow = desNode.systemNode
          ? desNode.systemNode.dataFlow
          : false;
        let endInputPorts = desNode.systemNode
          ? desNode.systemNode.inputPorts
          : [];
        if (endDataFlow) {
          let endPosX = this.getXPos(
            desNode.x,
            endInputPorts.length,
            v.desIndex
          );

          this.$set(link[k], 'endPosX', endPosX);
        } else {
          this.$set(
            link[k],
            'endPosX',
            desNode && desNode.x ? desNode.x + 90 : null
          );
        }
      });

      this.setLink(link);
    },

    // 获取链接的x的坐标点位置
    getXPos (x, len, i) {
      let posX;
      // 默认重点
      posX = x + 90;
      // 流数据，根据长度和下标
      if (len > 0) {
        posX = x + (180 / (len + 1)) * (i + 1);
      }

      return posX;
    },

    /**
     * 链接的改变
     * @param data 链接线的数据
     */
    linkChange (data) {
      let item = {
        start: data.id,
        end: false,
        startPosX: this.node[data.id].x + 90,
        startPosY: this.node[data.id].y,
        endPosX: data.x,
        endPosY: data.y,
        srcIndex: null,
        desIndex: null
      };

      // 数据流组件
      if (data.flowNode) {
        let { index } = data.flowNode;
        item.srcIndex = index;

        let { outputPorts } = this.node[data.id].systemNode;
        item.startPosX = this.getXPos(
          this.node[data.id].x,
          outputPorts.length,
          index
        );
      }
      let link = [...this.link];
      this.isNewLink ? this.$set(link, link.length - 1, item) : link.push(item);
      this.setLink(link);
    },

    /**
     * 连接方法
     * @param data 链接点的数据
     */
    isConnect (data) {
      // 连接的时候
      if (!data.bool) {
        // data表示当前的线的终点
        const startX = data.x;
        const startY = data.y;

        // 遍历所有节点
        for (let nodeItemId in this.node) {
          let endNode = this.node[nodeItemId];
          let endX = endNode.x;
          let endY = endNode.y;
          let nodeMaxX;
          let nodeMinX;

          // 只要不是自己，就有可能链接上
          if (nodeItemId !== data.id) {
            // 1、自己是数据流组件或者compatibleToDataFlow的，可以连数据流组件或者compatibleToDataFlow
            // 2、自己是普通的组件，只能连普通的组件，不可以连数据流组件
            // 3、如果2个都是流组件需要判断type
            // 4、数据流组件的每个桩只能连一条线
            let dataFlow = endNode.systemNode
              ? endNode.systemNode.dataFlow
              : false;
            let inputPorts = endNode.systemNode
              ? endNode.systemNode.inputPorts
              : [];
            let compatibleToDataFlow = endNode.systemNode
              ? endNode.systemNode.compatibleToDataFlow
              : false;
            if (data.flowNode || data.compatibleToDataFlow) {
              if (dataFlow || compatibleToDataFlow) {
                let circleTotal = inputPorts.length;
                for (let i = 0; i < circleTotal; i++) {
                  let nx = this.getXPos(0, circleTotal, inputPorts[i].index);
                  if (
                    startX > endX + nx - 20 &&
                    startX < endX + nx + 20 &&
                    startY > endY - 20 &&
                    startY < endY + 20
                  ) {
                    if (dataFlow) {
                      // 4数据流组件/保存数据 每个桩只能连一条线
                      let hasWire = false;
                      this.link.find(item => {
                        if (item.end === endNode.id && item.desIndex === i) {
                          hasWire = true;
                        }
                      });

                      if (hasWire) {
                        this.$Message.error('该节点只能连接一根线');
                      }

                      // 3判断type
                      if (data.flowNode) {
                        if (
                          data.flowNode.type === inputPorts[i].type &&
                          !hasWire
                        ) {
                          this.concentNode(data, nodeItemId, i);
                          break;
                        }
                      } else if (!hasWire) {
                        this.concentNode(data, nodeItemId, i);
                        break;
                      }
                    } else {
                      // 保存组件只能链接一个点
                      let hasWire = false;
                      if (endNode.pipelineTaskNodeType === wfCode.EXPORT) {
                        this.link.find(item => {
                          if (item.end === endNode.id) {
                            hasWire = true;
                          }
                        });

                        if (hasWire) {
                          this.$Message.error('该节点只能连接一根线');
                        }
                      }

                      if (!hasWire) {
                        this.concentNode(data, nodeItemId, i);
                        break;
                      }
                    }
                  }
                }
              } else {
                // 普通组件中间一个点的区域范围
                nodeMinX = 70;
                nodeMaxX = 110;
                if (
                  startX > endX + nodeMinX &&
                  startX < endX + nodeMaxX &&
                  startY > endY - 20 &&
                  startY < endY + 20 &&
                  !dataFlow
                ) {
                  // 保存组件只能链接一个点
                  let hasWire = false;
                  if (endNode.pipelineTaskNodeType === wfCode.EXPORT) {
                    this.link.find(item => {
                      if (item.end === endNode.id) {
                        hasWire = true;
                      }
                    });

                    if (hasWire) {
                      this.$Message.error('保存组件只能连接一根线');
                    }
                  }

                  if (!hasWire) {
                    this.concentNode(data, nodeItemId);
                  }
                }
              }
            } else {
              // 普通组件中间一个点的区域范围
              nodeMinX = 70;
              nodeMaxX = 110;
              if (
                startX > endX + nodeMinX &&
                startX < endX + nodeMaxX &&
                startY > endY - 20 &&
                startY < endY + 20 &&
                !dataFlow
              ) {
                // 保存组件只能链接一个点
                let hasWire = false;
                if (endNode.pipelineTaskNodeType === wfCode.EXPORT) {
                  this.link.find(item => {
                    if (item.end === endNode.id) {
                      hasWire = true;
                    }
                  });

                  if (hasWire) {
                    this.$Message.error('保存组件只能连接一根线');
                  }
                }

                if (!hasWire) {
                  this.concentNode(data, nodeItemId);
                }
              }
            }
          }
        }

        // 如果不在，则去除
        if (this.link.length && !this.link[this.link.length - 1].end) {
          let link = [...this.link];
          link.pop();
          this.setLink(link);
        }

        // 请求更新接口
        this.$nextTick(() => {
          this.nodeDragEnd();
        });
      }

      // 是在寻找点
      this.isNewLink = data.bool;
      this.flowNode = data.flowNode;
      this.compatibleToDataFlow = data.compatibleToDataFlow;
    },

    // 可以链接
    concentNode (data, nodeItemId, i) {
      let endNode = this.node[nodeItemId];
      let startPosX = this.node[data.id].x;
      let endPosX = this.node[endNode.id].x;
      let srcIndex = null;
      let desIndex = null;

      // 流数据
      if (i !== undefined) {
        srcIndex = data.flowNode ? data.flowNode.index : 0;
        desIndex = i;

        let endLen = endNode.systemNode.inputPorts.length;
        endPosX = this.getXPos(endPosX, endLen, desIndex);

        let { outputPorts } = this.node[data.id].systemNode;
        let startLen = outputPorts.length;
        startPosX = this.getXPos(startPosX, startLen, srcIndex);
      } else {
        startPosX = startPosX + 90;
        endPosX = endPosX + 90;
      }

      let item = {
        start: data.id, // 开始的nodeID
        end: endNode.id, // 结束的nodeID
        startPosX,
        startPosY: this.node[data.id].y,
        endPosX,
        endPosY: this.node[endNode.id].y,
        srcIndex,
        desIndex
      };

      // 找下是不是有这根线了
      let obj = {};
      obj = this.link.find(unit => {
        if (unit.start === data.id && unit.end === endNode.id) {
          if (desIndex >= 0 && desIndex !== unit.desIndex) {
            return false;
          } else {
            return true;
          }
        }
      });

      // 没有则新增线条
      if (!obj) {
        let link = [...this.link];
        this.$set(link, link.length - 1, item);
        this.setLink(link);

        this.$set(this.node, nodeItemId, {
          ...endNode,
          isConnected: true
        });
        this.setNode(this.node);
      }
    },

    // 从当前页面运行
    handelRun () {
      workflowApi
        .restartRun({
          pipelineId: this.pipelineId,
          pipelineJobId: this.pipelineJobId,
          startNodeId: this.contextNodeId
        })
        .then(res => {
          this.$Message.success('运行成功');
          this.cover = false;
          this.startRunWorkflow(true);
        })
        .catch(() => {
          this.cover = false;
        });
    },

    // 删除节点
    handelDel () {
      if (this.logRun) {
        this.$Message.error('工作流运行时不可删除节点');
        this.cover = false;
        return;
      }
      workflowApi
        .delNode({
          id: this.contextNodeId
        })
        .then(res => {
          let link = [...this.link];

          // 删除连线
          link = link.filter(item => {
            return (
              item.start !== this.contextNodeId &&
              item.end !== this.contextNodeId
            );
          });

          // 删除节点
          delete this.node[this.contextNodeId];

          for (let k in this.node) {
            let item = this.node[k];
            let arr = link.filter(vv => {
              return vv.end === item.id;
            });

            if (!arr.length) {
              this.$set(this.node, k, { ...item, isConnected: 0 });
              this.setNode(this.node);
            }
          }

          this.setLink(link);
          this.cover = false;
          this.activeNode = '';

          // 删除后回到主界面
          this.changeSettingType(wfCode.MAIN);
          this.$Message.success('删除成功');
        });
    },

    // 删除链接线
    handelLinkDel () {
      if (this.logRun) {
        this.$Message.error('工作流运行时不可删除线');
        this.linkcover = false;
        return;
      }

      let link = [...this.link];
      let sNodeId = this.linkContextNode.start;
      let eNodeId = this.linkContextNode.end;

      // 删除连线
      link = link.filter(item => {
        return item.start !== sNodeId || item.end !== eNodeId;
      });
      this.setLink(link);

      let sNode = this.node[sNodeId];
      if (sNode.wires.length > 0) {
        sNode.wires = sNode.wires.filter(item => {
          return item.srcId !== sNodeId || item.desId !== eNodeId;
        });
      }

      for (let k in this.node) {
        let item = this.node[k];
        let arr = link.filter(vv => {
          return vv.end === item.id;
        });

        if (!arr.length) {
          this.$set(this.node, k, { ...item, isConnected: 0 });
          this.setNode(this.node);
        }
      }

      let { pipelineTaskNodeType } = this.node[eNodeId];

      // 保存数据中的readonly
      if (
        pipelineTaskNodeType === wfCode.EXPORT &&
        this.currentNode &&
        this.currentNode.id === eNodeId &&
        this.$refs.drawerSetting
      ) {
        this.$refs.drawerSetting.setKeyValue('readonly', false);
      }

      this.setNode(this.node);
      // 请求到接口里去
      workflowApi.updateCustomNode({ ...this.node[sNodeId] }).then(res => {});

      // 隐藏面板
      this.linkcover = false;
    },

    /**
     * 点击节点右侧菜单的显示控制
     * @param param 返回的节点数据
     */
    coverShow (param) {
      let { e, item } = param;
      this.coverX = e.pageX - 60;
      this.coverY = e.pageY - 60;
      this.contextNodeId = item.id;
      this.cover = true;
      this.linkcover = false;
    },

    /**
     * 点击线条右侧菜单的显示控制
     * @param param 返回的线条数据
     */
    linkCoverShow (param) {
      let { e, item } = param;
      this.coverX = e.pageX - 60;
      this.coverY = e.pageY - 60;
      this.linkContextNode = item;
      this.linkcover = true;
      this.cover = false;
    },

    // 重命名节点
    handelRename () {
      this.newName = this.node[this.contextNodeId].name;
      this.newNameError = false;
      this.renameVisible = true;
    },

    // 组件树数据改造
    getComTrees () {
      workflowApi.getCategories().then(res => {
        let initTree = this.changeComTreeData(res.data);

        // 自定义节点放在最后
        let customIndex = initTree.findIndex(item => {
          return item.id === wfCode.MULTIPLEXT;
        });
        let customNode = JSON.parse(JSON.stringify(initTree[customIndex]));
        initTree.push(customNode);
        initTree.splice(customIndex, 1);

        this.setComTreeData(initTree);
      });
    },

    /**
     * 树的数据改造
     * @param data 原始树数据
     */
    changeComTreeData (data) {
      for (let i = 0; i < data.length; i++) {
        let item = data[i];

        // 数据流组件的icon
        if (item.title === '数据流组件') {
          item.children.forEach((child, index) => {
            child.icon = 'icon-dataflow';
          });
        }

        if (item.children.length > 0) {
          this.changeComTreeData(item.children);
        }

        if (item.taskNodeDTOs.length > 0) {
          item.taskNodeDTOs.forEach((unit, order) => {
            switch (unit.categoryId) {
              // 数据输入
              case wfCode.IMPORT:
                unit.nodeIcon = 'icon-import';
                break;
              // 数据输出
              case wfCode.EXPORT:
                unit.nodeIcon = 'icon-export';
                break;
              default:
                unit.nodeIcon = 'icon-datas';
            }

            if (item.icon) {
              unit.nodeIcon = item.icon;
            }
            item.children.push(unit);
          });
        }

        delete item.taskNodeDTOs;
      }

      return data;
    },

    // 点击面板显示工作流属性
    svgContainClick () {
      this.changeSettingType(wfCode.MAIN);
      this.activeNode = '';
      this.cover = false;
      this.linkcover = false;

      this.setCurrentNode(null);
    },

    /**
     * 重绘
     * @param msg 重绘后的提示
     */
    redrawNodes (msg) {
      workflowApi
        .redrawWorkflow({
          id: this.pipelineId
        })
        .then(res => {
          let node = {};
          let link = [];
          let wiresTotal = [];

          if (res.data) {
            res.data.forEach(item => {
              let { location, wires } = item;

              node[item.id] = Object.assign({}, item, {
                x: (location && location.x) || 0,
                y: (location && location.y) || 0,
                status: ''
              });

              if (wires && wires.length) {
                wires.forEach(item => {
                  wiresTotal.push(item);
                });
              }

              // 自动数据封装
              let { pipelineTaskNodeType, argValues, args } = node[item.id];
              this.addAutoDatas(pipelineTaskNodeType, argValues, args);
            });

            // 图标的设置
            for (let i in node) {
              let { pipelineTaskNodeType, systemNode } = node[i];
              switch (pipelineTaskNodeType) {
                // 数据输入
                case wfCode.IMPORT:
                  node[i].nodeIcon = 'icon-import';
                  break;
                // 数据输出
                case wfCode.EXPORT:
                  node[i].nodeIcon = 'icon-export';
                  break;
                default:
                  node[i].nodeIcon = 'icon-datas';
              }

              // 流组件
              if (systemNode && systemNode.dataFlow) {
                node[i].nodeIcon = 'icon-dataflow';
              }
            }

            this.setNode(node);

            // link的改变
            wiresTotal.forEach(item => {
              let { srcIndex, desIndex, srcId, desId } = item;
              let startNode = this.node[srcId];
              let desNode = this.node[desId];

              let obj = {
                srcIndex,
                desIndex,
                start: srcId,
                end: desId,
                startPosX: startNode.x + 90,
                startPosY: startNode.y,
                endPosX: desNode && desNode.x ? desNode.x + 90 : null,
                endPosY: desNode && desNode.y ? desNode.y : null
              };

              // 起始点的流数据设置
              if (startNode.systemNode) {
                let { dataFlow, outputPorts } = startNode.systemNode;
                if (dataFlow) {
                  obj.startPosX = this.getXPos(
                    startNode.x,
                    outputPorts.length,
                    srcIndex
                  );
                }
              }

              // 末尾点的流数据设置
              let endDataFlow =
                desNode && desNode.systemNode
                  ? desNode.systemNode.dataFlow
                  : false;
              let endInputPorts =
                desNode && desNode.systemNode
                  ? desNode.systemNode.inputPorts
                  : [];
              if (endDataFlow) {
                obj.endPosX = this.getXPos(
                  desNode.x,
                  endInputPorts.length,
                  desIndex
                );
              }
              if (startNode && desNode) {
                link.push(obj);
              }
            });

            let newLink = [];

            // 去重
            for (var i = 0; i < link.length; i++) {
              var flag = true;
              for (var j = 0; j < newLink.length; j++) {
                if (
                  link[i].start === newLink[j].start &&
                  link[i].end === newLink[j].end &&
                  link[i].srcIndex === newLink[j].srcIndex &&
                  link[i].desIndex === newLink[j].desIndex
                ) {
                  flag = false;
                }
              }
              if (flag) {
                newLink.push(link[i]);
              }
            }

            this.setLink(newLink);
            this.computedCanvas();
            if (msg) {
              this.$Message.success(msg);
            }
          }
        });
    },

    /**
     * 塞入自动补全数据中
     * @param pipelineTaskNodeType 节点的类型
     * @param argValues 节点的值
     * @param args 节点的值数组
     */
    addAutoDatas (pipelineTaskNodeType, argValues, args) {
      // 导出数据
      if (pipelineTaskNodeType === wfCode.EXPORT) {
        try {
          let argValues2 = JSON.parse(argValues);
          if (
            argValues2 &&
            argValues2.sourceType === 'file' &&
            argValues2.sourceConfig &&
            argValues2.sourceConfig.length > 2
          ) {
            this.setAutoDatas(argValues2.sourceConfig);
          }
        } catch (err) {}
      }

      // 通用数据
      if (pipelineTaskNodeType === wfCode.COMMON) {
        try {
          let argValues2 = JSON.parse(argValues);
          args.forEach(item => {
            let type = item.formEditType;
            if (type === 'file' || type === 'string') {
              if (item.defaultValue && item.defaultValue.length > 2) {
                this.setAutoDatas(item.defaultValue);
              }
              if (
                argValues2 &&
                argValues2[item.name] &&
                argValues2[item.name].length > 2
              ) {
                this.setAutoDatas(argValues2[item.name]);
              }
            }
          });
        } catch (err) {}
      }
    },

    /**
     * 鼠标按键事件
     */
    keyUpEvt (e) {
      e.preventDefault();
      const keyCode = e.keyCode || e.which;

      if (!this.currentNode) {
        return;
      }

      // 删除当前激活组件,并变成page
      if (keyCode === 46 && this.currentNode && this.currentNode.id) {
        // 如果在文本框中则不可以使用快捷键
        var t = e.srcElement ? e.srcElement.type : e.target.type; // 看当前所处环境
        if (t === 'text' || t === 'textarea') {
          return;
        }
        this.contextNodeId = this.currentNode.id;
        this.setCurrentNode(null);
        this.handelDel(true);
      }
    },

    // 获取工作流的状态
    getStatus () {
      workflowApi
        .getStatus({
          id: this.pipelineId
        })
        .then(res => {
          if (res && res.data) {
            let { status, id } = res.data;
            this.pipelineJobId = id;
            if (status === 'RUNNING') {
              this.startRunWorkflow(false);
            }
          }
        });
    }
  },

  mounted () {
    // 获取树节点
    this.getComTrees();

    // 收缩导航栏
    this.setNavCollapsed(true);

    // 切换至工作流属性
    this.changeSettingType(wfCode.MAIN);

    this.$nextTick(() => {
      // 获取数据并重绘
      this.redrawNodes();
      // 画布大小控制
      window.addEventListener('resize', this.resize);
    });

    // pipelineId的获取
    this.pipelineId = this.$route.query.pipelineId;

    // 获取工作流的运行状态
    this.getStatus();

    // 更新树的总线
    Bus.$on('updateWorkFlowTree', () => {
      this.getComTrees();
    });

    // 鼠标按键事件
    document.addEventListener('keyup', this.keyUpEvt);
  },

  beforeDestroy () {
    window.removeEventListener('resize', this.resize);
    document.removeEventListener('keyup', this.keyUpEvt);
    document.body.removeAttribute('style');
    clearInterval(this.logTimer);
    this.logTimer = null;

    Bus.$off('updateWorkFlowTree');
  }
};
</script>

<style rel="stylesheet/less" lang="less" scoped>
@themeColor: #1890ff;
.page-workflowdrawer {
  height: 100%;
  .drawer-left,
  .drawer-container,
  .drawer-right {
    background: #ffffff;
    float: left;
    border-right: #dddddd 1px solid;
    box-sizing: border-box;
    height: 100%;
    transition: width 0.5s;
  }
  .drawer-header {
    border-bottom: 1px solid #e8e8e8;

    .ivu-btn {
      margin: 10px;
    }
  }
  .drawer-left {
    width: 280px;
    .sub-nav {
      width: 40px;
      background: #515a6e;
      height: 100%;
      float: left;
      color: #fff;
      li {
        text-align: center;
        line-height: 50px;
        margin-bottom: 2px;
        cursor: pointer;
        &:hover,
        &.active {
          background: @themeColor;
        }
      }
    }
    .tree-container {
      float: left;
      height: 100%;
      width: ~"calc(100% - 50px)";
      padding: 20px;
      box-sizing: border-box;
      overflow-y: auto;
      .no-data {
        line-height: 40px;
        text-align: center;
        font-size: 14px;
        color: #666666;
      }
    }
  }
  .drawer-container {
    position: relative;
    width: calc(72% - 280px);
    background: #fafcff;
    cursor: pointer;
    .drawer-container-bottom {
      position: absolute;
      width: 100%;
      left: 0;
      bottom: 0;
      border-radius: 4px;
      background-color: #fff;
      .ivu-icon-md-desktop {
        position: absolute;
        font-size: 18px;
        z-index: 999;
        bottom: 5px;
        right: 5px;
        cursor: pointer;
        &.top-desktop {
          bottom: unset;
          top: 6px;
        }
      }
      .log-content {
        margin-bottom: 10px;
        padding-left: 10px;
        line-height: 28px;
        max-height: 300px;
        overflow-y: auto;
        overflow-x: hidden;
        white-space: normal;
      }
    }
    .drawer-container-top,
    .svg-contain {
      width: 100%;
      height: 100%;
      overflow-y: auto;
      overflow-x: hidden;
    }

    .drawer-container-top200 {
      height: calc(100% - 200px);
    }

    .drawer-container-top300 {
      height: calc(100% - 300px);
    }
  }
  .drawer-right {
    width: 28%;
    overflow-y: auto;
  }
}
.drag {
  position: absolute;
  z-index: -99;
  opacity: 0;
  transition: transform 0.5s 0.001s, opacity 0.02s 0.5s, z-index 0.01s 0.5s;

  &.active {
    transition: none;
  }

  &.show {
    opacity: 1;
    z-index: 10;
  }

  .component-drag-proxy {
    position: absolute;
    z-index: 2147483647;
    cursor: move;
    width: 157px;
    height: 35px;
    box-sizing: border-box;
    border: 1px dashed #007bff;
    font-size: 12px;
    padding-left: 8px;
    padding-right: 10px;
    border-radius: 5px;

    span {
      font-size: 14px;
      line-height: 35px;
    }
  }
}

.ui-cover {
  position: absolute;
  z-index: 1999;
  background: transparent;
  ul {
    margin: 0;
    padding: 5px 0;
    min-width: 160px;
    position: absolute;
    background-color: rgba(255, 255, 255, 0.98);
    border: 1px solid #eee;
    box-shadow: 5px 5px 30px rgba(0, 0, 0, 0.1);
    border-radius: 2px;
    user-select: none;
    li {
      position: relative;
      font-size: 12px;
      color: #333;
      height: 26px;
      line-height: 26px;
      padding-left: 30px;
      padding-right: 10px;
      cursor: pointer;
      transition: all 0.1s ease;
      &:hover {
        background: #e8f6ff;
      }
    }
  }
}

.drawer-operate {
  position: absolute;
  left: 10px;
  top: 4px;
  .ivu-icon-md-arrow-dropright-circle,
  .ivu-icon-md-square {
    font-size: 18px;
    position: relative;
    left: -3px;
    top: -2px;
  }
  .icon-stop {
    position: relative;
    left: -3px;
    top: 2px;
  }
}

.drawer-operate-right {
  position: absolute;
  right: 10px;
  top: 4px;
  .ivu-icon-ios-cloud-download-outline {
    font-size: 18px;
    position: relative;
    left: -3px;
    top: -2px;
  }
}

.log-wrapper {
  min-height: 200px;
  .text-log {
    line-height: 28px;
    padding: 0 14px;
  }
}

.workflow-footer {
  margin-top: 10px;
  text-align: right;
}
</style>

<style lang="less">
.workflow-tree {
  padding-left: 10px;
  .ivu-tree-arrow {
    position: relative;
    float: left;
    top: 2px;
  }
}

.workflow-tree-node {
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-left: 16px;
  cursor: pointer;
  &.border-node {
    border: 1px solid #007bff;
    border-radius: 4px;
    line-height: 28px;
    height: 28px;
    .workflow-tree-node-main {
      top: 6px;
    }
    .icon-add {
      font-size: 14px;
      position: relative;
      top: -4px;
      left: 6px;
    }
    &.add-node {
      width: 128px;
      margin-left: 34px;
    }
  }
}
.workflow-tree-node-main {
  user-select: none;
  display: inline-block;
  max-width: 100px;
  text-overflow: ellipsis;
  overflow: hidden;
  position: relative;
  top: 3px;
  left: 8px;
  &.selected {
    border-radius: 2px;
    background: #eee;
  }

  .ivu-icon {
    font-size: 18px;
  }
}
.workflow-tree-node-add-item {
  padding: 10px 5px;
}
.workflow-rename-model {
  .ivu-modal-mask,
  .ivu-modal-wrap {
    z-index: 2000 !important;
  }
  .rename-error {
    color: #f5222d;
    margin-top: 4px;
  }
  .rename-footer {
    margin-top: 20px;
    text-align: right;
  }
}
.workflowdrawer-table {
  .ivu-table-row {
    cursor: pointer;
  }

  .ivu-table-row-highlight td {
    background-color: #1890ff !important;
    color: #fff;
  }

  .ivu-table-row-highlight .ivu-table-cell span{
    color: #fff;
  }
}
.drawer-container-bottom {
  .log-wrapper {
    .ivu-tabs-nav-wrap {
      background: #e6e8ea;
    }
    .ivu-badge-count {
      line-height: 16px;
      height: 16px;
      text-align: center;
      padding: 0;
      left: 0px;
      top: -5px;
      min-width: 16px;
    }
  }
}
</style>
